﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Xml;
using pub.Functions;
using scheduler.Model;

namespace scheduler
{
    public class RemindChecker
    {
        private string todoListFile;

        public RemindChecker(string todoListFile)
        {
            this.todoListFile = todoListFile;
        }

        public void Check(DateTime lastCheckTime, DateTime checkTime)
        {
            var doc = new XmlDocument();
            try
            {
                doc.Load(todoListFile);
            }
            catch (XmlException e)
            {
                var text = File.ReadAllText(todoListFile).Trim();
                if (text.Length == 0)
                {
                    return;
                }
                else
                {
                    throw e;
                }
            }
            var reminderEls = doc.GetElementsByTagName("reminder");
            for (var n = 0; n < reminderEls.Count; n++)
            {
                var reminderEl = reminderEls[n];
                var info = CheckReminder(reminderEl, lastCheckTime, checkTime);
                if (info != null)
                {
                    Remind(info, reminderEl);
                }
            }
            Console.WriteLine("?");
        }

        private void Remind(ReminderInfo reminderInfo, XmlNode reminderEl)
        {
            IReminder reminder;
            if (reminderInfo.Type == ReminderType.Email)
            {
                reminder = new EmailReminder();
            }
            else
            {
                reminder = new PopupReminder();
            }
            var todoEl = reminderEl.ParentNode.ParentNode as XmlElement;
            var todoInfo = ReadTodoInfo(todoEl);
            reminder.Info = reminderInfo;
            reminder.TodoInfo = todoInfo;
            reminder.Execute();
        }

        private TodoInfo ReadTodoInfo(XmlElement todoEl)
        {
            var info = new TodoInfo();
            info.File = todoListFile;
            
            info.Title = todoEl["title"].GetText();
            
            var pathTitles = new List<string>();
            var el = todoEl;
            while (el != null)
            {
                if(el.Name == "todo")
                {
                    pathTitles.Insert(0, el["title"].GetText());
                }
                el = el.ParentNode as XmlElement;
            }
            info.PathTitles = pathTitles.ToArray();

            info.Due = todoEl["due"].GetText();
            info.Description = todoEl["description"].GetText();
            return info;
        }

        private ReminderInfo CheckReminder(XmlNode reminderEl, DateTime lastCheckTime,
                                           DateTime checkTime)
        {
            var timingEl = reminderEl["timing"];
            var sTimingType = timingEl.GetAttribute("type");
            var timingType = (TimingType) Enum.Parse(typeof (TimingType), sTimingType, true);
            var onTime = false;
            var timingValue = timingEl.InnerText.Trim();
            if (timingType == TimingType.At)
            {
                DateTime at;

                try
                {
                    at = DateTime.ParseExact(timingValue, "yyyy-MM-dd HH:mm",
                            CultureInfo.InvariantCulture);
                }
                catch(Exception e)
                {
                    Debug.Print("bad datetime format: " + timingValue);
                    return null;
                }

                if (at <= checkTime && at > lastCheckTime)
                {
                    onTime = true;
                }
            }
            else if (timingType == TimingType.Every)
            {
                var everyStr = timingValue;

                EveryInfo everyInfo;
                try
                {
                    everyInfo = TimingFuncs.ParseEveryInfo(everyStr);
                }
                catch (Exception e)
                {
                    Debug.Print("bad every value: " + timingValue);
                    return null;
                }
                //
                var lastTime = GetRepetitionHistory(everyStr);
                var theTime = TimingFuncs.GetNextRepetition(everyInfo, lastTime);

                if (theTime <= checkTime) // && theTime > lastCheckTime)
                {
                    onTime = true;
                    SetRepetitionHistory(everyStr, checkTime);
                }
            }
            else if (timingType == TimingType.Before)
            {
                //
                var todoEl = reminderEl.ParentNode.ParentNode;
                var due = todoEl["due"].InnerText.Trim();
                if (!string.IsNullOrEmpty(due))
                {
                    DateTime dueTime;
                    DateTime theTime;
                    try
                    {
                        dueTime = DateTime.ParseExact(due,
                            "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
                        theTime = TimingFuncs.Before(dueTime, timingValue);
                    }
                    catch (Exception e)
                    {
                        Debug.Print("bad before value: " + timingValue + " before " + due);
                        return null;
                    }
                    if (theTime <= checkTime && theTime > lastCheckTime)
                    {
                        onTime = true;
                    }
                }
            }
            if (!onTime)
            {
                return null;
            }

            var info = ReadReminderInfo(reminderEl);

            return info;
        }

        private void SetRepetitionHistory(string everyStr, DateTime theTime)
        {
            var sTime = theTime.ToString("yyyy-MM-dd HH:mm:ss");
            App.Settings["RepetitionHistory"][todoListFile + "|" + everyStr].StringValue = sTime;
        }

        private DateTime GetRepetitionHistory(string everyStr)
        {
            var sTime = App.Settings["RepetitionHistory"][todoListFile + "|" + everyStr].StringValue;
            if (string.IsNullOrEmpty(sTime))
            {
                return new DateTime();
            }
            else
            {
                return DateTime.ParseExact(sTime, "yyyy-MM-dd HH:mm:ss",
                        CultureInfo.InvariantCulture);
            }
        }

        private ReminderInfo ReadReminderInfo(XmlNode reminderEl)
        {
            var sType = reminderEl["type"].InnerText;
            var type = (ReminderType) Enum.Parse(typeof (ReminderType), sType, true);

            ReminderInfo info;
            if (type == ReminderType.Email)
            {
                var emailInfo = new EmailReminderInfo();

                emailInfo.To = reminderEl["to"].InnerText.Trim();

                info = emailInfo;
            }
            else
            {
                info = new ReminderInfo();
            }
            info.Type = type;


            return info;
        }
    }
}